#include "os_mblock.h"
#include "os_mem.h"
#include "os_sched.h"

#if OS_MBLOCK_ENABLE

#if !OS_MBLOCK_DBG_PRINT_ENABLE
#undef os_dbg
#define os_dbg(fmt, ...) do {}while (0)
#endif

/**
 * 存储块初始化
 * @param mblock 存储块结构
 * @param mem 存储起始地址
 * @param blk_size 存储块大小
 * @param blk_cnt 存储块的数量
 * @param flags 初始化相关参数
 * @return 初始化结果
*/
static os_err_t mblock_init (os_mblock_t * mblock, void * mem, uint16_t blk_size, uint16_t blk_cnt, int flags) {
    // 初始化等待结构
    os_err_t err = os_event_init(&mblock->event, OS_EVENT_TYPE_MBLOCK, flags);
    if (err < 0) {
        os_dbg("create mblock event failed: %s.", "inti f");
        return err;
    }

    mblock->mem = mem;
    mblock->blk_cnt = 0;
    mblock->list = (void *)0;
    mblock->blk_size = blk_size;

    // 构建存储块链表
	mem_link_t * start = (mem_link_t *)mem;
    for (int i = 0; i < blk_cnt; i++) {
        // 插入到块链表头部中
        start->next = mblock->list;
        mblock->list = start;

        // 取下一个块
        start = (mem_link_t *)((uint8_t *)start + blk_size);
        mblock->blk_cnt++;
    }
#if OS_MBLOCK_INFO_ENABLE || OS_ASSERT_ENABLE
    mblock->blk_size = blk_size;
    mblock->blk_max = mblock->blk_cnt;
#endif
    return OS_ERR_OK;
}

/**
 * 存储块初始化
 * @param mblock 存储块结构
 * @param mem 存储起始地址
 * @param blk_size 存储块大小
 * @param blk_cnt 存储块的数量
 * @return 初始化结果
 * @note 注意，传入的块大小和数量需要考虑到内存对齐等问题
*/
os_err_t os_mblock_init (os_mblock_t * mblock, void * mem, uint16_t blk_size, uint16_t blk_cnt) {
    os_param_failed(mblock == OS_NULL, OS_ERR_PARAM);
    os_param_failed(mem == OS_NULL, OS_ERR_PARAM);
    os_param_failed(blk_cnt <= 0, OS_ERR_PARAM);
    os_param_failed(blk_size < sizeof(mem_link_t *), OS_ERR_PARAM);

    // 内存对齐
    int total_size = blk_size * blk_cnt;
    blk_size = MEM_HIGH_ALIGED(blk_size, MEM_ALIGN_BYTES);
    blk_cnt = total_size / blk_size;
    os_param_failed(blk_cnt <= 0, OS_ERR_PARAM);

    return mblock_init(mblock, mem, blk_size, blk_cnt, 0);
}

/**
 * 释放存储块
 */
os_err_t os_mblock_uninit (os_mblock_t * mblock) {
    os_param_failed(mblock == OS_NULL, OS_ERR_PARAM);

    // 唤醒所有任务后，尝试进行调试
    os_int_status_t status = os_sched_int_lock();
    int cnt = os_event_wait_cnt(&mblock->event);
    os_event_uninit(&mblock->event);
    os_sched_int_unlock(status);

    // 有任务唤醒时，进行调度，以便运行更高优先级的任务
    if (cnt > 0) {
        os_sched_run();
    }

    return OS_ERR_OK;
}

#if OS_MEM_ENABLE
/**
 * 分配存储块结构
 * @param blk_size 存储块大小
 * @param blk_cnt 存储块的数量
 * @return 创建的存储块
 */
os_mblock_t * os_mblock_create (uint16_t blk_size, uint16_t blk_cnt) {
    os_param_failed(blk_cnt <= 0, OS_NULL);
    os_param_failed(blk_size < sizeof(void *), OS_NULL);

    // 分配控制结构
    os_mblock_t * mblock = os_mem_malloc(sizeof(os_mblock_t));
    if (mblock == OS_NULL) {
        os_dbg("error: alloc mblock failed");
        return OS_NULL;
    }

    // 分配存储空间，注意size对齐到int大小的边界
    blk_size = MEM_HIGH_ALIGED(blk_size, MEM_ALIGN_BYTES);
    void * mem = os_mem_malloc(blk_size * blk_cnt);
    if (mem == OS_NULL) {
        os_dbg("error: alloc mem failed");
        os_mem_free(mblock);
        return OS_NULL;
    }

    // 初始化控制块结构
    os_err_t err = mblock_init(mblock, mem, blk_size, blk_cnt, OS_FLAG_MEM_HEAP);
    if (err < 0) {
        os_dbg("error: init mblock failed.");
        os_mem_free(mblock);
        os_mem_free(mem);
        return OS_NULL;
    }

    return mblock;
}

/**
 * 释放一个存储块
 * @param 需要释放的存储块
 */
os_err_t os_mblock_free (os_mblock_t * mblock) {
    os_param_failed(mblock == OS_NULL, OS_ERR_PARAM);

    os_mblock_uninit(mblock);
    if (mblock->event.flags & OS_FLAG_MEM_HEAP) {
        os_mem_free(mblock->mem);
        os_mem_free(mblock);
    }
    return OS_ERR_OK;
}
#endif

#if OS_MBLOCK_INFO_ENABLE
/**
 * 获取空闲的内存块数量
 * @param mblock 存储块结构
 * @return 空闲内存块数量
*/
uint16_t os_mblock_blk_cnt (os_mblock_t * mblock) {
    os_param_failed(mblock == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    uint16_t cnt = mblock->blk_cnt;
    os_sched_int_unlock(status);
    return cnt;
}

/**
 * 获取空闲的内存块大小
 * @param mblock 存储块结构
 * @return 块大小
*/
uint16_t os_mblock_blk_size (os_mblock_t * mblock) {
    os_param_failed(mblock == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    uint16_t cnt = mblock->blk_size;
    os_sched_int_unlock(status);
    return cnt;
}

/**
 * 获取等待内存块分配的任务数量
 * @param mblock 内存控制块
 * @return 等待内存块的任务数量
 */
uint16_t os_mblock_tasks(os_mblock_t * mblock) {
    os_param_failed(mblock == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    uint16_t cnt = os_event_wait_cnt(&mblock->event);
    os_sched_int_unlock(status);
    return cnt;
}
#endif

/**
 * 申请一个内存块
 * @param mblock 内存块
 * @param ms 等待时间
 * @param err 错误码
 * @return 分配的内存块
*/
void * os_mblock_wait(os_mblock_t * mblock, int ms, os_err_t * p_err) {
    os_param_failed_exec(mblock == OS_NULL, OS_NULL, if (p_err) *p_err = OS_ERR_PARAM);

    void * mem = OS_NULL;

    os_int_status_t status = os_sched_int_lock();
    if (mblock->blk_cnt > 0) {
        // 如果有内存块，则返回
        mem_link_t * mem_link = mblock->list;
        mblock->list = mem_link->next;
        mblock->blk_cnt--;
        os_sched_int_unlock(status);

        // 设置返回值
        if (p_err) {
            *p_err = OS_ERR_OK;
        }
        mem = (void *)mem_link;
    } else if ((ms < 0) || (os_core.int_nested > 0)) {
        os_sched_int_unlock(status);
        // 没有，且不等
        os_dbg("no mem block and no wait\n");
        if (p_err) {
            *p_err = OS_ERR_NONE;
        }
    } else {
        // 没有，则等待可用的资源
        os_event_wait(&mblock->event, OS_NULL, ms);
        os_sched_int_unlock(status);
        os_sched_run();

        // 当任务唤醒后，任务的event_wait应该已经有了相关块
        os_task_t * self = os_task_self();
        if (p_err) {
            *p_err = self->event_wait.err;
        }

        // 有可能不成功，比如超时了
        if (self->event_wait.err == OS_ERR_OK) {
            mem = self->event_wait.reason;
        }
    }
    return mem;
}

/**
 * 释放内存块
 * @param mblock 存储块
 * @param mem 内存块
 * @return 释放的结果
 */
os_err_t os_mblock_release (os_mblock_t * mblock, void * mem) {
    os_param_failed(mblock == OS_NULL, OS_ERR_PARAM);
    os_param_failed(mem == OS_NULL, OS_ERR_PARAM);
    os_param_failed(mem < mblock->mem, OS_ERR_PARAM);
    os_param_failed((uint8_t *)mem >= ((uint8_t *)mblock->mem + mblock->blk_max * mblock->blk_size), OS_ERR_PARAM);

    os_int_status_t status = os_sched_int_lock();
    os_task_t * task = os_event_notify(&mblock->event);
    if (task == OS_NULL) {
        // 没有任务在等待，将mem插入到空闲队列中
        mem_link_t * block = (mem_link_t *)mem;
        block->next = mblock->list;
        mblock->list = block;
        mblock->blk_cnt++;

        os_sched_int_unlock(status);
    } else {
        // 将mem交给对方，通过设置reason指向的消息
        task_wait_t * wait = &task->event_wait;
        wait->err = OS_ERR_OK;
        wait->reason = mem;

        os_sched_int_unlock(status);

        // 有任务等，通知调度器，看看是否要进行任务切换
        os_sched_run();
    }

    return OS_ERR_OK;
}
#endif // OS_MBLOCK_ENABLE
